﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections;

namespace AmiEmu
{
    public class SortableBindingList<T> : BindingList<T>, IBindingListView
    {
        private PropertyComparerCollection<T> m_sorts;

        public SortableBindingList()
            : base()
        {

        }

        public SortableBindingList(IList<T> a_list)
            : base(a_list)
        {

        }

        protected override bool IsSortedCore
        {
            get 
            { 
                return m_sorts != null; 
            }
        }

        protected override void RemoveSortCore()
        {
            m_sorts = null;
        }

        protected override bool SupportsSortingCore
        {
            get
            { 
                return true; 
            }
        }

        protected override ListSortDirection SortDirectionCore
        {
            get
            {
                return m_sorts == null ? ListSortDirection.Ascending :
                    m_sorts.PrimaryDirection;
            }
        }

        protected override PropertyDescriptor SortPropertyCore
        {
            get 
            { 
                return m_sorts == null ? null : m_sorts.PrimaryProperty; 
            }
        }

        protected override void ApplySortCore(PropertyDescriptor a_prop, ListSortDirection a_direction)
        {
            ListSortDescription[] arr = { new ListSortDescription(a_prop, a_direction) };
            ApplySort(new ListSortDescriptionCollection(arr));
        }

        public void ApplySort(ListSortDescriptionCollection a_sortCollection)
        {
            bool oldRaise = RaiseListChangedEvents;
            RaiseListChangedEvents = false;

            try
            {
                PropertyComparerCollection<T> tmp = new PropertyComparerCollection<T>(a_sortCollection);
                List<T> items = new List<T>(this);
                items.Sort(tmp);
                int index = 0;
                foreach (T item in items)
                    SetItem(index++, item);
                m_sorts = tmp;
            }
            finally
            {
                RaiseListChangedEvents = oldRaise;
                ResetBindings();
            }
        }

        string IBindingListView.Filter
        {
            get 
            { 
                throw new NotImplementedException(); 
            }
            set 
            { 
                throw new NotImplementedException(); 
            }
        }

        void IBindingListView.RemoveFilter()
        {
            throw new NotImplementedException();
        }

        ListSortDescriptionCollection IBindingListView.SortDescriptions
        {
            get 
            { 
                return m_sorts.Sorts; 
            }
        }

        bool IBindingListView.SupportsAdvancedSorting
        {
            get 
            { 
                return true; 
            }
        }

        bool IBindingListView.SupportsFiltering
        {
            get 
            { 
                return false; 
            }
        }
    }

    public class PropertyComparerCollection<T> : IComparer<T>
    {
        private readonly ListSortDescriptionCollection m_sorts;
        private readonly PropertyComparer<T>[] m_comparers;

        public ListSortDescriptionCollection Sorts
        {
            get 
            { 
                return m_sorts; 
            }
        }

        public PropertyComparerCollection(ListSortDescriptionCollection a_sorts)
        {
            if (a_sorts == null) 
                throw new ArgumentNullException("sorts");
            m_sorts = a_sorts;
            List<PropertyComparer<T>> list = new List<PropertyComparer<T>>();
            foreach (ListSortDescription item in a_sorts)
            {
                list.Add(new PropertyComparer<T>(item.PropertyDescriptor, 
                    item.SortDirection == ListSortDirection.Descending));
            }
            m_comparers = list.ToArray();
        }

        public PropertyDescriptor PrimaryProperty
        {
            get
            {
                return m_comparers.Length == 0 ? null : m_comparers[0].Property;
            }
        }

        public ListSortDirection PrimaryDirection
        {
            get
            {
                return m_comparers.Length == 0 ? ListSortDirection.Ascending
                    : m_comparers[0].Descending ? ListSortDirection.Descending
                    : ListSortDirection.Ascending;
            }
        }

        int IComparer<T>.Compare(T x, T y)
        {
            int result = 0;
            for (int i = 0; i < m_comparers.Length; i++)
            {
                result = m_comparers[i].Compare(x, y);
                if (result != 0) 
                    break;
            }
            return result;
        }

        public class PropertyComparer<V> : IComparer<V>
        {
            private readonly bool m_descending;
            private readonly PropertyDescriptor m_property;

            public bool Descending 
            { 
                get 
                { 
                    return m_descending; 
                } 
            }

            public PropertyDescriptor Property 
            { 
                get 
                { 
                    return m_property; 
                } 
            }

            public PropertyComparer(PropertyDescriptor a_property, bool a_descending)
            {
                if (a_property == null)
                    throw new ArgumentNullException("property");
                m_descending = a_descending;
                m_property = a_property;
            }

            public int Compare(V x, V y)
            {
                int value = Comparer.Default.Compare(m_property.GetValue(x), m_property.GetValue(y));
                return m_descending ? -value : value;
            }
        } 
    }
}
